package com.eva.framework.common.spring;

import com.eva.framework.common.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class SpELService {

    // SpEL表达式解析器
    private final ExpressionParser parser = new SpelExpressionParser();

    /**
     * 基于AOP切点构建SpEL表达式上下文
     *
     * @param point 切点
     * @return EvaluationContext
     */
    public EvaluationContext buildContext(ProceedingJoinPoint point) {
        log.debug("构建SpEL表达式上下文");
        // 获取会话适配器对象，需要适配Shiro和Spring Security两个框架
        Object sessionAdapter = null;
        try {
            sessionAdapter = Utils.SpringContext.getBean("ssSessionAdapter");
        } catch (Exception e) {
            try {
                sessionAdapter = Utils.SpringContext.getBean("shiroSessionAdapter");
            } catch (Exception ignored) {
            }
        }
        if (sessionAdapter == null) {
            log.warn("未能获取到SessionAdapter实例，SpEL将无法通过loginUser读取登录用户信息");
        }
        // 获取用户信息
        Object loginUser = null;
        if (sessionAdapter != null) {
            try {
                /*
                 无论是Spring Security的SsSessionAdapter还是Spring Security的ShiroSessionAdapter，
                 获取登录用户信息时，方法均是getLoginUser()，因为他们都是对SessionAdapter的实现
                */
                Method getLoginUserMethod = sessionAdapter.getClass().getMethod("getLoginUser");
                loginUser = getLoginUserMethod.invoke(sessionAdapter);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        // 获取方法参数
        Object[] args = point.getArgs();
        MethodSignature signature = (MethodSignature) point.getSignature();
        String[] parameterNames = signature.getParameterNames();

        // 创建表达式上下文并填充方法参数作为数据
        StandardEvaluationContext context = new StandardEvaluationContext();
        Map<String, Object> variables = new HashMap<>();
        variables.put("loginUser", loginUser);
        for (int i = 0; i < parameterNames.length; i++) {
            variables.put(parameterNames[i], args[i]);
        }
        context.setVariables(variables);
        log.debug("上下文参数：{}", variables);
        return context;
    }

    /**
     * 解析缓存键表达式
     *
     * @param express 表达式
     * @param context SpEL表达式上下文
     */
    public String parse(String express, EvaluationContext context) {
        log.debug("开始解析表达式：{}", express);
        if (!express.contains("{")) {
            log.debug("表达式解析完成：{}", express);
            return express;
        }
        StringBuilder result = new StringBuilder();
        int start = 0;
        while (true) {
            int exprStart = express.indexOf('{', start);
            if (exprStart == -1) {
                result.append(express.substring(start));
                break;
            }
            result.append(express, start, exprStart);
            int exprEnd = express.indexOf('}', exprStart);
            if (exprEnd == -1) {
                throw new IllegalArgumentException("表达式解析失败，表达式格式不正确: " + express);
            }
            String expr = express.substring(exprStart + 1, exprEnd);
            Object value;
            try {
                value = parser.parseExpression(expr).getValue(context);
            } catch (SpelEvaluationException e) {
                if (!expr.contains("#")) {
                    throw new IllegalArgumentException("表达式解析失败，缺少'#'变量符号：" + expr);
                }
                throw new IllegalArgumentException("表达式解析失败，未找到相关对象或字段：" + expr, e);
            } catch (Exception e) {
                throw new IllegalArgumentException("表达式解析失败：" + expr, e);
            }
            result.append(value);
            start = exprEnd + 1;
        }
        log.debug("表达式解析完成：{}", result);
        return result.toString();
    }
}
